<template>
  <div>
    <div class="select" @click.stop="show">
      <el-input
        v-model="selectLabel"
        :suffix-icon="showOption ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"
        readonly
        placeholder="请选择"
        style="width: 200px"
      ></el-input>
    </div>
    <div>
      <el-input v-model="selectVal" style="display: none"></el-input>
    </div>
    <div class="option-box" v-show="showOption" @click.stop>
      <el-input
        v-model.trim="searchVal"
        size="small"
        placeholder="请输入查询"
        style="padding: 5px"
      ></el-input>
      <!-- 
        滚动事件使用截流的16.6实现存在体验不佳的情况，
        轻轻滚动滚轮时，下拉选项有轻微抖动的感觉。
        最终还是用requestAnimationFrame来处理
        抖动的效果消除
       -->
      <div
        ref="container"
        class="v-container"
        :style="'height:' + height + 'px'"
        @scroll="handleScrollWithAnimationFrame"
      >
        <div class="scroll" :style="'height:' + totalHeight + 'px'"></div>
        <div
          :style="'width:100%;height:' + height + 'px;top:' + scroolTop + 'px'"
          class="item-box"
        >
          <div
            v-for="item in showList"
            :key="item.value"
            :style="'height:' + size + 'px'"
            :class="['item', { isSelected: item.value === selectVal }]"
            @click="select(item)"
          >
            {{ item.label }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { debounce, throttle } from "@/utils/utils.js";
export default {
  props: {
    value: {
      type: String,
      required: false,
    },
    list: {
      type: Array,
      default: () => [],
    },
    size: {
      type: Number,
      default: 40,
    },
    num: {
      type: Number,
      default: 20,
    },
  },
  data() {
    return {
      selectLabel: "",
      selectVal: "",
      showOption: false,
      searchVal: "",
      filterList: [],
      start: 0,
      end: this.num,
      scroolTop: 0,
      debounceFn: null,
      throttleFn: null,
    };
  },
  computed: {
    height() {
      return this.size * this.num;
    },
    totalHeight() {
      return this.size * this.filterList.length;
    },
    showList() {
      return this.filterList.slice(this.start, this.end);
    },
  },
  created() {
    this.debounceFn = debounce(() => {
      this.filterCallback(this.searchVal);
      if (this.$refs.container) {
        this.$refs.container.scrollTo(0, 0);
      }
    }, 1000);
    /**
     * 此处截流设置的再次执行时间大于浏览器屏幕刷新频率时，在滚动操作很快的时候，会出现白屏的bug
     * 所以解决方案有两种
     * 1.截流设置的再次执行时间小于浏览器刷新频率
     * 2.直接使用requestAnimationFrame
     * eg: handleScrollWithAnimationFrame
     */
    this.throttleFn = throttle(this.scrollCallback, 16.6);
  },
  mounted() {
    window.addEventListener("click", () => {
      this.showOption = false;
    });
  },
  destroyed() {
    window.removeEventListener("click", () => {
      this.showOption = false;
    });
  },
  watch: {
    searchVal: {
      handler() {
        if (this.searchVal) {
          this.debounceFn(this.searchVal);
        } else {
          this.filterList = [...this.list];
        }
      },
      immediate: true,
    },
    list: {
      handler() {
        this.selectLabel = this.getSelectLabel();
        if (this.searchVal) {
          this.filterCallback(this.searchVal);
        } else {
          this.filterList = [...this.list];
        }
      },
      deep: true,
    },
    value() {
      this.selectVal = this.value;
      this.selectLabel = this.getSelectLabel();
    },
  },
  methods: {
    getSelectLabel() {
      if (this.selectVal) {
        const obj = this.list.find((item) => item.value === this.selectVal);

        if (!obj || !obj.label) {
          return "";
        } else {
          return obj.label;
        }
      } else {
        return "";
      }
    },
    show() {
      this.showOption = true;
    },
    filterCallback(val) {
      this.filterList = this.list.filter((item) =>
        item.label.toUpperCase().includes(val.toUpperCase())
      );
    },
    handleScrollWithAnimationFrame() {
      requestAnimationFrame(this.scrollCallback);
    },
    handleScroll() {
      this.throttleFn();
    },
    scrollCallback() {
      this.scroolTop = this.$refs.container.scrollTop;
      this.start = Math.floor(this.scroolTop / this.size);
      this.end = this.start + this.num;
    },
    select(item) {
      this.selectVal = item.value;
      this.$emit("input", item.value);
      this.showOption = false;
    },
  },
};
</script>
<style scoped>
.option-box {
  border: 1px solid #eee;
  margin-top: 10px;
}
.v-container {
  overflow-y: scroll;
  position: relative;
}
.item-box {
  position: absolute;
  top: 0;
  left: 0;
  padding: 5px;
  text-align: left;
}
.item {
  line-height: 30px;
  cursor: pointer;
}
.item:hover {
  background-color: rgba(150, 150, 150, 0.3);
}
.isSelected {
  color: #409eff;
}
</style>
